# frozen_string_literal: true

require "spec_helper"

describe Split::Persistence::DualAdapter do
  let(:context) { "some context" }

  let(:logged_in_adapter_instance) { double }
  let(:logged_in_adapter) do
    Class.new.tap { |c| allow(c).to receive(:new) { logged_in_adapter_instance } }
  end
  let(:logged_out_adapter_instance) { double }
  let(:logged_out_adapter) do
    Class.new.tap { |c| allow(c).to receive(:new) { logged_out_adapter_instance } }
  end

  context "when fallback_to_logged_out_adapter is false" do
    context "when logged in" do
      subject do
        described_class.with_config(
          logged_in: lambda { |context| true },
          logged_in_adapter: logged_in_adapter,
          logged_out_adapter: logged_out_adapter,
          fallback_to_logged_out_adapter: false
        ).new(context)
      end

      it "#[]=" do
        expect(logged_in_adapter_instance).to receive(:[]=).with("my_key", "my_value")
        expect_any_instance_of(logged_out_adapter).not_to receive(:[]=)
        subject["my_key"] = "my_value"
      end

      it "#[]" do
        expect(logged_in_adapter_instance).to receive(:[]).with("my_key") { "my_value" }
        expect_any_instance_of(logged_out_adapter).not_to receive(:[])
        expect(subject["my_key"]).to eq("my_value")
      end

      it "#delete" do
        expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect_any_instance_of(logged_out_adapter).not_to receive(:delete)
        expect(subject.delete("my_key")).to eq("my_value")
      end

      it "#keys" do
        expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] }
        expect_any_instance_of(logged_out_adapter).not_to receive(:keys)
        expect(subject.keys).to eq(["my_value"])
      end
    end

    context "when logged out" do
      subject do
        described_class.with_config(
          logged_in: lambda { |context| false },
          logged_in_adapter: logged_in_adapter,
          logged_out_adapter: logged_out_adapter,
          fallback_to_logged_out_adapter: false
        ).new(context)
      end

      it "#[]=" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:[]=)
        expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value")
        subject["my_key"] = "my_value"
      end

      it "#[]" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:[])
        expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { "my_value" }
        expect(subject["my_key"]).to eq("my_value")
      end

      it "#delete" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:delete)
        expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect(subject.delete("my_key")).to eq("my_value")
      end

      it "#keys" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:keys)
        expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] }
        expect(subject.keys).to eq(["my_value", "my_value2"])
      end
    end
  end

  context "when fallback_to_logged_out_adapter is true" do
    context "when logged in" do
      subject do
        described_class.with_config(
          logged_in: lambda { |context| true },
          logged_in_adapter: logged_in_adapter,
          logged_out_adapter: logged_out_adapter,
          fallback_to_logged_out_adapter: true
        ).new(context)
      end

      it "#[]=" do
        expect(logged_in_adapter_instance).to receive(:[]=).with("my_key", "my_value")
        expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value")
        expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { nil }
        subject["my_key"] = "my_value"
      end

      it "#[]" do
        expect(logged_in_adapter_instance).to receive(:[]).with("my_key") { "my_value" }
        expect_any_instance_of(logged_out_adapter).not_to receive(:[])
        expect(subject["my_key"]).to eq("my_value")
      end

      it "#delete" do
        expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect(subject.delete("my_key")).to eq("my_value")
      end

      it "#keys" do
        expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] }
        expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] }
        expect(subject.keys).to eq(["my_value", "my_value2"])
      end
    end

    context "when logged out" do
      subject do
        described_class.with_config(
          logged_in: lambda { |context| false },
          logged_in_adapter: logged_in_adapter,
          logged_out_adapter: logged_out_adapter,
          fallback_to_logged_out_adapter: true
        ).new(context)
      end

      it "#[]=" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:[]=)
        expect(logged_out_adapter_instance).to receive(:[]=).with("my_key", "my_value")
        expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { nil }
        subject["my_key"] = "my_value"
      end

      it "#[]" do
        expect_any_instance_of(logged_in_adapter).not_to receive(:[])
        expect(logged_out_adapter_instance).to receive(:[]).with("my_key") { "my_value" }
        expect(subject["my_key"]).to eq("my_value")
      end

      it "#delete" do
        expect(logged_in_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect(logged_out_adapter_instance).to receive(:delete).with("my_key") { "my_value" }
        expect(subject.delete("my_key")).to eq("my_value")
      end

      it "#keys" do
        expect(logged_in_adapter_instance).to receive(:keys) { ["my_value"] }
        expect(logged_out_adapter_instance).to receive(:keys) { ["my_value", "my_value2"] }
        expect(subject.keys).to eq(["my_value", "my_value2"])
      end
    end
  end

  describe "when errors in config" do
    before { described_class.config.clear }
    let(:some_proc) { -> { } }

    it "when no logged in adapter" do
      expect {
        described_class.with_config(
          logged_in: some_proc,
          logged_out_adapter: logged_out_adapter
        ).new(context)
      }.to raise_error(StandardError, /:logged_in_adapter/)
    end

    it "when no logged out adapter" do
      expect {
        described_class.with_config(
          logged_in: some_proc,
          logged_in_adapter: logged_in_adapter
        ).new(context)
      }.to raise_error(StandardError, /:logged_out_adapter/)
    end

    it "when no logged in detector" do
      expect {
        described_class.with_config(
          logged_in_adapter: logged_in_adapter,
          logged_out_adapter: logged_out_adapter
        ).new(context)
      }.to raise_error(StandardError, /:logged_in$/)
    end
  end
end
